
// 17.27 in 802.1w-2001

#include "802_1w_2001_procedures.h"
#include "bridge.h"
#include "port.h"
#include "assert.h"

enum
{
	UNDEFINED,
	TRANSMIT_INIT,
	TRANSMIT_PERIODIC,
	TRANSMIT_CONFIG,
	TRANSMIT_TCN,
	TRANSMIT_RSTP,
	IDLE,
};

// ============================================================================

const char* PortTransmit_802_1w_2001_GetStateName (SM_STATE state)
{
	switch (state)
	{
		case TRANSMIT_INIT:		return "TRANSMIT_INIT";
		case TRANSMIT_PERIODIC:	return "TRANSMIT_PERIODIC";
		case TRANSMIT_CONFIG:	return "TRANSMIT_CONFIG";
		case TRANSMIT_TCN:		return "TRANSMIT_TCN";
		case TRANSMIT_RSTP:		return "TRANSMIT_RSTP";
		case IDLE:				return "IDLE";
		default:				return "(undefined)";
	}
}

// ============================================================================

SM_STATE PortTransmit_802_1w_2001_CheckConditions (BRIDGE* bridge, int givenPort, int givenTree, SM_STATE state)
{
	assert (givenPort != -1);
	assert (givenTree != -1);
	
	PORT* port = bridge->ports [givenPort];
	PORT_TREE* portTree = port->trees [givenTree];

	// ------------------------------------------------------------------------
	// Check global conditions.
	
	if (bridge->BEGIN)
	{
		if (state == TRANSMIT_INIT)
		{
			// The entry block for this state has been executed already.
			return 0;
		}
		
		return TRANSMIT_INIT;
	}
	
	// ------------------------------------------------------------------------
	// Check exit conditions from each state.

	if (state == TRANSMIT_INIT)
		return IDLE;

	if (state == TRANSMIT_PERIODIC)
		return IDLE;
		
	if (state == TRANSMIT_CONFIG)
		return IDLE;
	
	if (state == TRANSMIT_TCN)
		return IDLE;
	
	if (state == TRANSMIT_RSTP)
		return IDLE;
	
	if (state == IDLE)
	{
		if (port->sendRSTP && port->newInfo && (port->txCount < bridge->TransmitHoldCount) && (port->helloWhen !=0) && ((portTree->role == PORT_ROLE_DESIGNATED) || (portTree->role == PORT_ROLE_ROOT)))
			return TRANSMIT_RSTP;
		
		if (!port->sendRSTP && port->newInfo && (port->txCount < bridge->TransmitHoldCount) && (portTree->role == PORT_ROLE_ROOT) && (port->helloWhen != 0))
			return TRANSMIT_TCN;
		
		if (!port->sendRSTP && port->newInfo && (port->txCount < bridge->TransmitHoldCount) && (portTree->role == PORT_ROLE_DESIGNATED) && (port->helloWhen != 0))
			return TRANSMIT_CONFIG;
		
		if (port->helloWhen == 0)
			return TRANSMIT_PERIODIC;
		
		return 0;
	}
	
	assert (false);
	return 0;
}

// ============================================================================

void PortTransmit_802_1w_2001_InitState (BRIDGE* bridge, int givenPort, int givenTree, SM_STATE state)
{
	assert (givenPort != -1);
	assert (givenTree != -1);
	
	PORT* port = bridge->ports [givenPort];
	PORT_TREE* portTree = port->trees [givenTree];

	// ------------------------------------------------------------------------
	// If we're here, it means the state has just been changed. Let's initialize the new state.
	
	if (state == TRANSMIT_INIT)
	{
		port->newInfo = false;
		port->helloWhen = 0;
		port->txCount = 0;
	}	
	else if (state == TRANSMIT_PERIODIC)
	{
		port->newInfo = port->newInfo || ((portTree->role == PORT_ROLE_DESIGNATED) || ((portTree->role == PORT_ROLE_ROOT) && (portTree->tcWhile !=0)));
		port->helloWhen = bridge->trees [CIST_INDEX]->BridgeTimes.HelloTime;
	}	
	else if (state == IDLE)
	{
	}	
	else if (state == TRANSMIT_CONFIG)
	{
		port->newInfo = false;
		txConfig_802_1w_2001 (bridge, givenPort); port->txCount +=1;
		port->tcAck = false;
	}	
	else if (state == TRANSMIT_TCN)
	{
		port->newInfo = false;
		txTcn_802_1w_2001 (bridge, givenPort); port->txCount +=1;
	}	
	else if (state == TRANSMIT_RSTP)
	{
		port->newInfo = false;
		txRstp_802_1w_2001 (bridge, givenPort); port->txCount +=1;
		port->tcAck = false;
	}	
	else
		assert (false);
}
